import * as fs from 'fs';

export interface MagicValueResult {
  success: boolean;
  message: string;
  magicValuesFound: MagicValue[];
  filesModified: string[];
  constantsAdded: string[];
  errors?: string[];
}

export interface MagicValue {
  value: string;
  type: 'string' | 'number' | 'boolean';
  locations: Array<{
    line: number;
    context: string;
  }>;
  suggestedConstantName: string;
  suggestedConstantValue: string;
}

export class MagicValueExtractor {
  
  /**
   * 扫描并提取魔法值
   */
  async extractMagicValues(filePath: string, autoFix: boolean = false): Promise<MagicValueResult> {
    const result: MagicValueResult = {
      success: false,
      message: '',
      magicValuesFound: [],
      filesModified: [],
      constantsAdded: [],
      errors: []
    };

    try {
      const content = fs.readFileSync(filePath, 'utf-8');
      const magicValues = this.findMagicValues(content);
      
      result.magicValuesFound = magicValues;

      if (autoFix && magicValues.length > 0) {
        const fixedContent = this.replaceMagicValues(content, magicValues);
        fs.writeFileSync(filePath, fixedContent);
        result.filesModified.push(filePath);
        result.constantsAdded = magicValues.map(mv => mv.suggestedConstantName);
      }

      result.success = true;
      result.message = autoFix 
        ? `提取了 ${magicValues.length} 个魔法值为常量`
        : `发现 ${magicValues.length} 个魔法值`;

    } catch (error) {
      result.errors?.push(`魔法值提取失败: ${error}`);
      result.message = `魔法值提取失败: ${error}`;
    }

    return result;
  }

  private findMagicValues(content: string): MagicValue[] {
    const magicValues: MagicValue[] = [];
    const lines = content.split('\n');
    
    // 排除的常见值（不算魔法值）
    const excludeValues = new Set([
      '0', '1', '-1', 'true', 'false', 'null', '""', "''",
      '0.0', '1.0', '0L', '1L', '0f', '1f'
    ]);

    for (let i = 0; i < lines.length; i++) {
      const line = lines[i];
      const lineNumber = i + 1;

      // 查找字符串魔法值
      this.findStringMagicValues(line, lineNumber, magicValues, excludeValues);
      
      // 查找数字魔法值  
      this.findNumberMagicValues(line, lineNumber, magicValues, excludeValues);
    }

    // 合并相同的魔法值
    return this.mergeDuplicateMagicValues(magicValues);
  }

  private findStringMagicValues(line: string, lineNumber: number, magicValues: MagicValue[], excludeValues: Set<string>) {
    // 匹配双引号字符串，排除空字符串和单字符
    const stringPattern = /"([^"]{2,})"/g;
    let match;

    while ((match = stringPattern.exec(line)) !== null) {
      const value = match[1];
      const fullMatch = match[0];
      
      // 排除日志消息、异常消息等
      if (!this.isLogOrErrorMessage(line) && 
          !excludeValues.has(fullMatch) &&
          this.shouldExtractString(value)) {
        
        magicValues.push({
          value: fullMatch,
          type: 'string',
          locations: [{
            line: lineNumber,
            context: line.trim()
          }],
          suggestedConstantName: this.generateConstantName(value, 'string'),
          suggestedConstantValue: `private static final String ${this.generateConstantName(value, 'string')} = ${fullMatch};`
        });
      }
    }
  }

  private findNumberMagicValues(line: string, lineNumber: number, magicValues: MagicValue[], excludeValues: Set<string>) {
    // 匹配数字（整数和小数）
    const numberPattern = /\b(\d{2,}(?:\.\d+)?[fFdDlL]?)\b/g;
    let match;

    while ((match = numberPattern.exec(line)) !== null) {
      const value = match[1];
      
      if (!excludeValues.has(value) && 
          !this.isDateOrTimeConstant(line) &&
          !this.isArraySizeDeclaration(line)) {
        
        const numericValue = parseFloat(value);
        const javaType = value.includes('.') ? 'double' : 'int';
        
        magicValues.push({
          value: value,
          type: 'number',
          locations: [{
            line: lineNumber,
            context: line.trim()
          }],
          suggestedConstantName: this.generateConstantName(value, 'number'),
          suggestedConstantValue: `private static final ${javaType.toUpperCase()} ${this.generateConstantName(value, 'number')} = ${value};`
        });
      }
    }
  }

  private isLogOrErrorMessage(line: string): boolean {
    const logPatterns = [
      /log\./i, /logger\./i, /System\.out/i, /System\.err/i,
      /throw new/i, /Exception/i, /Error/i
    ];
    return logPatterns.some(pattern => pattern.test(line));
  }

  private shouldExtractString(value: string): boolean {
    // 排除URL、SQL、配置路径等
    const excludePatterns = [
      /^https?:\/\//, /^\//, /^[a-zA-Z]:\\/, // URLs and paths
      /SELECT|INSERT|UPDATE|DELETE/i, // SQL
      /\$\{.*\}/, // 配置占位符
      /^[A-Z_]+$/ // 全大写（可能已经是常量）
    ];
    
    return !excludePatterns.some(pattern => pattern.test(value)) && value.length > 1;
  }

  private isDateOrTimeConstant(line: string): boolean {
    return /\b(year|month|day|hour|minute|second|millisecond|timestamp)\b/i.test(line);
  }

  private isArraySizeDeclaration(line: string): boolean {
    return /new\s+\w+\[\d+\]/.test(line) || /\[\d+\]/.test(line);
  }

  private generateConstantName(value: string, type: string): string {
    if (type === 'string') {
      // 从字符串内容生成常量名
      const sanitized = value
        .replace(/[^a-zA-Z0-9\s]/g, ' ')
        .replace(/\s+/g, '_')
        .toUpperCase();
      
      return sanitized.length > 20 ? sanitized.substring(0, 20) : sanitized;
    } else {
      // 数字常量需要根据上下文推断
      return `CONSTANT_${value.replace(/\./g, '_').replace(/[fFdDlL]$/, '')}`;
    }
  }

  private mergeDuplicateMagicValues(magicValues: MagicValue[]): MagicValue[] {
    const merged = new Map<string, MagicValue>();

    for (const mv of magicValues) {
      if (merged.has(mv.value)) {
        const existing = merged.get(mv.value)!;
        existing.locations.push(...mv.locations);
      } else {
        merged.set(mv.value, mv);
      }
    }

    return Array.from(merged.values());
  }

  private replaceMagicValues(content: string, magicValues: MagicValue[]): string {
    let modifiedContent = content;
    const lines = modifiedContent.split('\n');
    
    // 1. 在类的开始位置添加常量声明
    const classLineIndex = lines.findIndex(line => /class\s+\w+/.test(line));
    if (classLineIndex !== -1) {
      const constantDeclarations = magicValues.map(mv => `    ${mv.suggestedConstantValue}`);
      
      // 找到类开始的大括号后插入常量
      const openBraceIndex = lines.findIndex((line, index) => 
        index > classLineIndex && line.includes('{'));
      
      if (openBraceIndex !== -1) {
        constantDeclarations.reverse().forEach(declaration => {
          lines.splice(openBraceIndex + 1, 0, '', declaration);
        });
      }
    }

    // 2. 替换魔法值为常量引用
    modifiedContent = lines.join('\n');
    for (const mv of magicValues) {
      const escapedValue = mv.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
      const regex = new RegExp(escapedValue, 'g');
      modifiedContent = modifiedContent.replace(regex, mv.suggestedConstantName);
    }

    return modifiedContent;
  }
}